package com.liruan.rest.aop;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.liruan.rest.dto.RequestDTO;

/**
 * 拦截GET请求，将GET请求中的参数转换为所拦截的controller方法所需要的参数(该参数必须实现{@link RequestDTO})。
 * 要利用本拦截器, GET请求对应的controller方法必须被{@link GetMapping}注解，方法只能有一个实现了{@link RequestDTO}的参数。
 * 目前的实现中，该参数中的字段类型或者类型参数的取值只能为{@link Long}、{@link Integer}、{@link String}中的一种，其他类型的字段不会被注入
 */
@Aspect
@Component
public class DTOCovertInterceptor
{
	private static final Logger LOGGER = LoggerFactory.getLogger(DTOCovertInterceptor.class);

	@Pointcut("execution(* com.liruan.rest.controller..*(..)) && @annotation(org.springframework.web.bind.annotation.GetMapping)")
	public void dtoCovertPointcut()
	{
	}

	private void doInject(Object arg, Class<?> cls)
	{
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
		Field field = arg.getClass().getDeclaredFields()[0];
		if(cls == null)
		{
			cls = field.getType();
		}
		Map<String, String[]> paramMap = request.getParameterMap();
		if (paramMap.containsKey(field.getName()))
		{
			String string = paramMap.get(field.getName())[0];
			Object value = null;
			try
			{
				if (cls == Long.class)
				{
					value = Long.parseLong(string);
				}
				else if (cls == Integer.class)
				{
					value = Integer.parseInt(string);
				}
				else if (cls == String.class)
				{
					value = string;
				}
				else
				{
					LOGGER.info("not supported type to inject:{}", cls.getName());
				}

				LOGGER.info("inject to field:{} type:{}", field.getName(), cls.getName());
				field.setAccessible(true);
				field.set(arg, value);
			}
			catch (Exception e)
			{
				LOGGER.info("error when inject filed:{} in class:{] of type:{}", field.getDeclaringClass().getName(), field.getName(), cls.getName(), e);
			}
		}
	}

	private void inject(Object[] args, Method method)
	{
		if (args == null || args.length == 0)
		{
			LOGGER.info("方法:{} 没有参数传入", method.getName());
			return;
		}

		Type type = method.getGenericParameterTypes()[0];
		if (type instanceof ParameterizedType)
		{
			Class<?> paramType = method.getParameterTypes()[0];
			if (RequestDTO.class.isAssignableFrom(paramType))
			{
				LOGGER.info("prepare inject {}", paramType.getName());
				ParameterizedType parameterizedType = (ParameterizedType) type;
				Type typeArg = parameterizedType.getActualTypeArguments()[0];
				if (typeArg instanceof ParameterizedType)
				{
					LOGGER.info("type:{} is ParameterizedType skip inject", typeArg.getTypeName());
					return;
				}
				Class<?> typeArgCls = (Class<?>) typeArg;
				doInject(args[0], typeArgCls);
			}
			else
			{
				LOGGER.info("not RequestDTO, skip inject");
			}
		}
		else
		{
			Class<?> cls = (Class<?>) type;
			if (RequestDTO.class.isAssignableFrom(cls))
			{
				LOGGER.info("prepare inject {}", cls.getName());
				doInject(args[0], null);
			}
			else
			{
				LOGGER.info("not RequestDTO, skip inject");
			}
		}
		//	Class<?> cls = ();
	}

	@Around("dtoCovertPointcut()")
	public Object doCovert(ProceedingJoinPoint pjp) throws Throwable
	{
		Object[] args = pjp.getArgs();
		Method method = ((MethodSignature) pjp.getSignature()).getMethod();
		inject(args, method);
		return pjp.proceed(args);
	}
}
